package kuzmenkov.applet;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.Reader;
import java.util.logging.Logger;

import nanoxml.XMLElement;
import nanoxml.XMLParseException;

/**
 * The class represents a XML description of a screen
 * 
 * @author Michael Kuzmenkov
 * 
 */
public class Screen {

	private Logger log = Logger.getLogger(this.getClass().getPackage()
			.getName());

	public static final String SCREEN_NODE = "nav:screen";
	public static final String ROOT_NODE = SCREEN_NODE;
	public static final String COLUMNS_ATT_OF_SCREEN = "columns";
	public static final String FIELD_COUNT_ATT_OF_SCREEN = "fieldCount";
	public static final String NAME_ATT_OF_SCREEN = "name";
	public static final String ROWS_ATT_OF_SCREEN = "rows";

	public static final String FIELDS_NODE = "nav:fields";

	public static final String FIELD_NODE = "nav:field";
	public static final String COLOR_ATT_OF_FIELD = "color";
	public static final String COLUMN_ATT_OF_FIELD = "column";
	public static final String LENGTH_ATT_OF_FIELD = "length";
	public static final String NAME_ATT_OF_FIELD = "name";
	public static final String ROW_ATT_OF_FIELD = "row";

	private String name;
	private int columns;
	private int fieldCount;
	private int rows;
	private Image image;

	private Field[] fields;

	public Screen(String name, int columns, int rows, int fieldCount) {
		setName(name);
		setColumns(columns);
		setRows(rows);
		setFieldCount(fieldCount);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		if (name == null)
			name = "";
		this.name = name;
	}

	public int getColumns() {
		return columns;
	}

	public void setColumns(int columns) {
		if (columns <= 0)
			throw new IllegalArgumentException(
					"The quantity of the columns must be positive");
		this.columns = columns;
	}

	public int getRows() {
		return rows;
	}

	public void setRows(int rows) {
		if (rows <= 0)
			throw new IllegalArgumentException(
					"The quantity of the rows must be positive");
		this.rows = rows;
	}

	public int getFieldCount() {
		return fieldCount;
	}

	public void setFieldCount(int fieldCount) {
		if (fieldCount <= 0)
			throw new IllegalArgumentException(
					"The field count must be positive");
		this.fieldCount = fieldCount;
		this.fields = new Field[fieldCount];
	}

	public Field[] getFields() {
		return fields;
	}

	public void setFields(Field[] fields) {
		if (fields == null || fields.length != fieldCount)
			throw new IllegalArgumentException(
					"The quantity of the fields in the array is incorrect");
		this.fields = fields;
	}

	/**
	 * Loads the screen from the file by using the reader
	 * 
	 * @param xmlScreenReader
	 *            the reader
	 * @return the screen
	 * @throws IOException 
	 * @throws XMLParseException 
	 */
	public static Screen loadScreen(Reader xmlScreenReader) throws XMLParseException, IOException {
		if (xmlScreenReader == null)
			throw new IllegalArgumentException("The file reader is null");

		XMLElement xmlImage = new XMLElement();
		xmlImage.parseFromReader(xmlScreenReader);

		XMLScreenConverter processor = new XMLScreenConverter();
		Screen screen = processor.convert(xmlImage);

		assert (screen != null);
		return screen;
	}

	/**
	 * Creates the image of this screen
	 * 
	 * @param fontName
	 *            the name of the font in the image. It must belong to a
	 *            monospace family
	 * @param fontSize
	 *            the size of the font in the image. It must be positive
	 * @return the image
	 */
	public BufferedImage createImage(String fontName, int fontSize) {
		if (fontName == null || fontName.isEmpty())
			throw new IllegalArgumentException("The font name is null or empty");
		if (fontSize <=0 )
			throw new IllegalArgumentException("The font size is not positive: "+fontSize); 

		Font font = new Font(fontName, Font.PLAIN, fontSize);
		FontRenderContext frc = new FontRenderContext(new AffineTransform(),
				true, true);
		Rectangle2D bounds = font.getStringBounds("A", frc);
		int letterHeight = (int) bounds.getHeight();
		int letterWidth = (int) bounds.getWidth();
		log.fine("letterHeight = " + letterHeight + ", letterWidth = "
				+ letterWidth);

		int imageHeight = letterHeight * getRows() + 1;
		int imageWidth = letterWidth * getColumns() + 1;

		BufferedImage image = new BufferedImage(imageWidth, imageHeight,
				BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = (Graphics2D) image.getGraphics();

		g2d.setFont(font);
		drawImage(g2d, letterWidth, letterHeight, imageWidth, imageHeight);
		g2d.dispose();
		
		assert (image!=null);
		return image;
	}

	/**
	 * Sets up the instance of a Graphics2d to draw the image of this screen
	 * 
	 * @param g2d
	 *            the instance of a Graphics2D
	 * @param letterWidth
	 *            a width of a letter of the used font
	 * @param letterHeight
	 *            a height of a letter of the used font
	 * @param imageWidth
	 * @param imageHeight
	 */
	private void drawImage(Graphics2D g2d, int letterWidth, int letterHeight,
			int imageWidth, int imageHeight) {
		if (g2d == null)
			throw new IllegalArgumentException("The graphics is null");
		if (letterWidth<=0)
			throw new IllegalArgumentException("The width of a letter is not positive: "+letterWidth);
		if (letterHeight<=0)
			throw new IllegalArgumentException("The height of a letter is not positive: "+letterHeight);
		if (imageWidth<=0)
			throw new IllegalArgumentException("The width of the image is not positive");
		if (imageHeight<=0)
			throw new IllegalArgumentException("The height of the image is not positive");

		Field[] fields = getFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];
			int x = field.getColumn() * letterWidth;
			int y = field.getRow() * letterHeight;
			Color color = field.getColor();
			log.finer("color = " + color);
			String content = field.getContent();

			Color oldColor = g2d.getColor();
			g2d.setColor(color);

			g2d.drawString(content, x, y);

			g2d.setColor(oldColor);
		}

	}

	public void setImage(Image image) {
		this.image = image;
	}

	public Image getImage() {
		return image;
	}
}
